package com.linkallcloud.log.web.es;

import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.RangeQueryBuilder;
import org.elasticsearch.search.sort.SortBuilder;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.elasticsearch.core.query.NativeSearchQuery;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;

import com.linkallcloud.core.face.message.request.PageFaceRequest;
import com.linkallcloud.core.query.Operator;
import com.linkallcloud.core.query.Query;
import com.linkallcloud.core.query.rule.CompareRule;
import com.linkallcloud.core.query.rule.QueryRule;
import com.linkallcloud.core.util.Dates;

public class EsQueryHelper {

    @SuppressWarnings("rawtypes")
    public static NativeSearchQuery convertEsSearchQuery(PageFaceRequest pfr) {
        BoolQueryBuilder bqb = convertEsBoolQuery(pfr);
        PageRequest pr = PageRequest.of(pfr.getPageNum() - 1, pfr.getLength());
        SortBuilder sb = SortBuilders.scoreSort();
        if (pfr.getQuery().getOrderby() != null && pfr.getQuery().getOrderby().isOrderBySetted()) {
            sb = SortBuilders.fieldSort("doc." + pfr.getQuery().getOrderby().getOrderby());
            if ("asc".equals(pfr.getQuery().getOrderby().getOrderDir().toLowerCase())) {
                sb.order(SortOrder.ASC);
            } else {
                sb.order(SortOrder.DESC);
            }
        }
        NativeSearchQuery query = new NativeSearchQueryBuilder().withQuery(bqb).withPageable(pr).withSorts(sb).build();
        return query;
    }

    public static BoolQueryBuilder convertEsBoolQuery(PageFaceRequest pfr) {
        BoolQueryBuilder bqb = QueryBuilders.boolQuery();
        if (pfr.getQuery() != null && pfr.getQuery().getCnds() != null) {
            Query q = pfr.getQuery().toQuery();
            if (q.getRules() != null) {
                Map<String, RangeQueryBuilder> ranges = new HashMap<>();
                for (QueryRule r : q.getRules()) {
                    if (r instanceof CompareRule) {
                        CompareRule cr = (CompareRule) r;
                        if (Operator.eq.equals(cr.getOp())) {
                            bqb.filter(QueryBuilders.termsQuery(cr.getField(), cr.getValue()));
                        } else if (Operator.cn.equals(cr.getOp())) {
                            bqb.filter(QueryBuilders.matchQuery(cr.getField(), cr.getValue()));
                        } else if (Operator.ge.equals(cr.getOp())) {
                            RangeQueryBuilder range = parseCompareRange(cr, ranges);
                            range.gte(parseCompareValue(cr.getValue()));
                        } else if (Operator.gt.equals(cr.getOp())) {
                            RangeQueryBuilder range = parseCompareRange(cr, ranges);
                            range.gt(parseCompareValue(cr.getValue()));
                        } else if (Operator.le.equals(cr.getOp())) {
                            RangeQueryBuilder range = parseCompareRange(cr, ranges);
                            range.lte(parseCompareValue(cr.getValue()));
                        } else if (Operator.lt.equals(cr.getOp())) {
                            RangeQueryBuilder range = parseCompareRange(cr, ranges);
                            range.lt(parseCompareValue(cr.getValue()));
                        }
                    }
                }

                if (!ranges.isEmpty()) {
                    Iterator<String> itr = ranges.keySet().iterator();
                    while (itr.hasNext()) {
                        String field = itr.next();
                        RangeQueryBuilder range = ranges.get(field);
                        bqb.must(range);
                    }
                }
            }
        }

        return bqb;
    }

    private static RangeQueryBuilder parseCompareRange(CompareRule cr, Map<String, RangeQueryBuilder> ranges) {
        RangeQueryBuilder range = null;
        if (ranges.containsKey(cr.cField())) {
            range = ranges.get(cr.cField());
        } else {
            range = new RangeQueryBuilder(cr.cField());
            ranges.put(cr.cField(), range);
        }
        return range;
    }

    private static Object parseCompareValue(Object ruleValue) {
        if (ruleValue instanceof Date) {
            Date tmp = (Date) ruleValue;
            return Dates.formatDateTime(tmp);
        }
        return ruleValue;
    }

}
